<?php

declare(strict_types=1);

namespace DreamCat\ObjectValid\Annotation;

use Attribute;
use DreamCat\ObjectValid\Base\ValidAnnoInterface;
use DreamCat\ObjectValid\Utils\ReflectionUtils;
use JetBrains\PhpStorm\Immutable;
use ReflectionParameter;
use ReflectionProperty;

/**
 * 对数字的范围限制
 * @author vijay
 */
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER | Attribute::IS_REPEATABLE)]
#[Immutable]
class NumberScope implements ValidAnnoInterface
{
    /**
     * @param int|float|null $max 最大值，null表示不限制
     * @param int|float|null $min 最小值，null表示不限制
     * @param string|null $message 错误信息
     */
    public function __construct(
        private int|float|null $max = null,
        private int|float|null $min = null,
        private ?string $message = null
    ) {
    }

    /**
     * @return float|int|null 最大值，null表示不限制
     */
    public function getMax(): float|int|null
    {
        return $this->max;
    }

    /**
     * @return float|int|null 最小值，null表示不限制
     */
    public function getMin(): float|int|null
    {
        return $this->min;
    }

    /**
     * @return string|null 错误信息
     */
    public function getMessage(): ?string
    {
        return $this->message;
    }

    /**
     * @inheritDoc
     */
    public function errorMessage(ReflectionProperty|ReflectionParameter $reflection, mixed $value): string
    {
        if (!is_null($this->message)) {
            return $this->message;
        }
        $list = [];
        if ($this->max !== null) {
            $list[] = "不能大于 {$this->max}";
        }
        if ($this->min !== null) {
            $list[] = "不能小于 {$this->min}";
        }
        return ReflectionUtils::getReflectDesc($reflection) . " " . implode(" 且", $list);
    }

    /**
     * @inheritDoc
     */
    public function isValid(mixed $value): bool
    {
        if ($value === null) {
            return true;
        }
        if ($this->max !== null && $value > $this->max) {
            return false;
        }
        if ($this->min !== null && $value < $this->min) {
            return false;
        }
        return true;
    }
}

# end of file
